#if defined(MODVERSIONS)
#include <linux/modversions.h>
#endif
#include <linux/module.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
#	include <linux/netfilter/x_tables.h>
#	define ipt_register_match xt_register_match
#	define ipt_unregister_match xt_unregister_match
#	define ipt_match xt_match
#else
#	include <linux/netfilter_ipv4/ip_tables.h>
//#include <linux/netfilter_ipv4/ipt_ipp2p.h>
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) */

#include <linux/types.h>
#include <linux/skbuff.h>
//#include <linux/netfilter_ipv4/ip_tables.h>
#include "ipt_domain.h"
#include <net/udp.h>


#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
static bool
#else
static int
#endif
match(const struct sk_buff *skb, const struct net_device *in,
			const struct net_device *out,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
      const struct xt_match  *mymatch,
      const void *matchinfo,
      int offset,
      unsigned int myprotoff,
#else
      const void *matchinfo,
      int offset,
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
      const void *hdr,
      u_int16_t datalen,
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
      bool *hotdrop)
#else
      int *hotdrop)
#endif
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
    struct iphdr *ip = ip_hdr(skb);
#else
    struct iphdr *ip = skb->nh.iph;
#endif
	struct udphdr *udph;
	const struct ipt_domain_info *info = matchinfo;

	if (offset || ip->protocol != IPPROTO_UDP)
		return 0;

	udph = (char *)ip + ip->ihl*4;

	return ( (ntohs(udph->source) == 53 || ntohs(udph->dest) == 53)
				&& (udph->len >= (8 + 12 + info->len + 5))
				&& !strcmp(info->name,
				(char *)((void *)udph + ntohs(udph->len) - info->len - 5))
		);

}


#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
static bool
#else
static int
#endif
checkentry(const char *tablename, 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
           const void *ip,
           const struct xt_match *mymatch,
#else
           const struct ipt_ip *ip,
#endif
	   void *matchinfo,
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
	   unsigned int matchsize,
#endif

	unsigned int hook_mask)
{
//	if (matchsize != IPT_ALIGN(sizeof(struct ipt_domain_info)))
//		return 0;

	return 1;
}


static struct ipt_match domain_match = { 
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	{ NULL, NULL },
	"domain",
	&match,
	&checkentry,
	NULL,
	THIS_MODULE
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
	.name		= "domain",
	.match		= &match,
	.checkentry	= &checkentry,
	.me		= THIS_MODULE,
#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) */
	.name		= "domain",
	.match		= &match,
	.family         = AF_INET,
	.matchsize      = XT_ALIGN(sizeof(struct ipt_domain_info)),
	.checkentry	= &checkentry,
	.me		= THIS_MODULE,
#endif
};


static int __init init(void)
{
	return ipt_register_match(&domain_match);
}


static void __exit fini(void)
{
	ipt_unregister_match(&domain_match);
}


module_init(init);
module_exit(fini);


MODULE_AUTHOR("Platinum, bbs.chinaunix.net");
MODULE_DESCRIPTION("A module to match DOMAIN. VERSION: 0.0.2");
MODULE_LICENSE("GPL");
